import { Callout, Tabs } from "nextra/components"
import { Code } from "@/components/Code"

<img align="right" src="/img/providers/postmark.svg" height="64" width="96" />

# Postmark Provider

## Overview

The Postmark provider uses email to send "magic links" that contain URLs with verification tokens can be used to sign in.

Adding support for signing in via email in addition to one or more OAuth services provides a way for users to sign in if they lose access to their OAuth account (e.g. if it is locked or deleted).

The Postmark provider can be used in conjunction with (or instead of) one or more OAuth providers.

### How it works

On initial sign in, a **Verification Token** is sent to the email address provided. By default this token is valid for 24 hours. If the Verification Token is used within that time (i.e. by clicking on the link in the email) an account is created for the user and they are signed in.

If someone provides the email address of an _existing account_ when signing in, an email is sent and they are signed into the account associated with that email address when they follow the link in the email.

<Callout type="warning">
  The Postmark provider can be used with both JSON Web Token and database
  managed sessions, however **you must configure a database** to use it. It is
  not possible to enable email sign in without using a database.
</Callout>

## Configuration

1. First, you'll need to [add your domain](https://postmark.com/domains) to your Postmark account. This is required by Postmark and this domain of the address you use in the `from` provider option.

2. Next, you will have to generate an API key in the [Postmark Dashboard](https://postmark.com/api-keys). You can save this API key as the `AUTH_POSTMARK_KEY` environment variable.

```sh
AUTH_POSTMARK_KEY=abc
```

If you name your environment variable `AUTH_POSTMARK_KEY`, the provider will pick it up automatically and your Auth.js configuration object can be simpler. If you'd like to rename it to something else, however, you'll have to manually pass it into the provider in your Auth.js configuration.

<Code>
<Code.Next>

```ts filename="./auth.ts"
import NextAuth from "next-auth"
import Postmark from "next-auth/providers/postmark"

export const { handlers, auth, signIn, signOut } = NextAuth({
  adapter: ...,
  providers: [
    Postmark({
      // If your environment variable is named differently than default
      apiKey: AUTH_POSTMARK_KEY,
      from: "no-reply@company.com"
    }),
  ],
})
```

</Code.Next>
<Code.Qwik>
  
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Postmark from "@auth/qwik/providers/postmark"

export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
  () => ({
    providers: [
      Postmark({
        // If your environment variable is named differently than default
        apiKey: import.meta.env.AUTH_POSTMARK_KEY,
        from: "no-reply@company.com",
      }),
    ],
  })
)
```

</Code.Qwik>
<Code.Svelte>

```ts filename="./src/auth.ts"
import { SvelteKitAuth } from "@auth/sveltekit"
import Postmark from "@auth/sveltekit/providers/postmark"
import { env } from "$env/dynamic/prviate"

export const { handle, signIn, signOut } = SvelteKitAuth({
  adapter: ...,
  providers: [
    Postmark({
      // If your environment variable is named differently than default
      apiKey: env.AUTH_POSTMARK_KEY,
      from: "no-reply@company.com",
    }),
  ],
})
```

</Code.Svelte>
</Code>

4. Do not forget to setup one of the [database adapters](https://authjs.dev/getting-started/database) for storing the Email verification token.

5. You can now start the sign-in process with an email address at `/api/auth/signin`.

A user account (i.e. an entry in the `Users` table) will not be created for the user until the first time they verify their email address. If an email address is already associated with an account, the user will be signed in to that account when they click the link in magic link email and use up the verification token.

## Customization

### Email Body

You can fully customize the sign in email that is sent by passing a custom function as the `sendVerificationRequest` option to `Postmark()`.

```js {7} filename="./auth.ts"
import NextAuth from "next-auth"
import Postmark from "next-auth/providers/postmark"

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Postmark({
      server: process.env.EMAIL_SERVER,
      from: process.env.EMAIL_FROM,
      sendVerificationRequest({
        identifier: email,
        url,
        provider: { server, from },
      }) {
        // your function
      },
    }),
  ],
})
```

As an example, the following shows the source for our built-in `sendVerificationRequest()` method. Notice that we're rendering the HTML (`html()`) and making the network call (`fetch()`) to Postmark to actually do the sending here in this method.

```ts filename="./lib/authSendRequest.ts" {4, 14}
export async function sendVerificationRequest(params) {
  const { identifier: to, provider, url, theme } = params
  const { host } = new URL(url)
  const res = await fetch("https://api.postmark.com/emails", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${provider.apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      from: provider.from,
      to,
      subject: `Sign in to ${host}`,
      html: html({ url, host, theme }),
      text: text({ url, host }),
    }),
  })

  if (!res.ok)
    throw new Error("Postmark error: " + JSON.stringify(await res.json()))
}

function html(params: { url: string; host: string; theme: Theme }) {
  const { url, host, theme } = params

  const escapedHost = host.replace(/\./g, "&#8203;.")

  const brandColor = theme.brandColor || "#346df1"
  const color = {
    background: "#f9f9f9",
    text: "#444",
    mainBackground: "#fff",
    buttonBackground: brandColor,
    buttonBorder: brandColor,
    buttonText: theme.buttonText || "#fff",
  }

  return `
<body style="background: ${color.background};">
  <table width="100%" border="0" cellspacing="20" cellpadding="0"
    style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
    <tr>
      <td align="center"
        style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
        Sign in to <strong>${escapedHost}</strong>
      </td>
    </tr>
    <tr>
      <td align="center" style="padding: 20px 0;">
        <table border="0" cellspacing="0" cellpadding="0">
          <tr>
            <td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
                target="_blank"
                style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
                in</a></td>
          </tr>
        </table>
      </td>
    </tr>
    <tr>
      <td align="center"
        style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
        If you did not request this email you can safely ignore it.
      </td>
    </tr>
  </table>
</body>
`
}

// Email Text body (fallback for email clients that don't render HTML, e.g. feature phones)
function text({ url, host }: { url: string; host: string }) {
  return `Sign in to ${host}\n${url}\n\n`
}
```

<Callout type="info">
  If you want to generate great looking emails with React that are compatible
  with many email clients, check out [mjml](https://mjml.io) or
  [react-email](https://react.email)
</Callout>

### Verification Tokens

By default, we are generating a random verification token. You can define a `generateVerificationToken` method in your provider options if you want to override it:

```ts filename="./auth.ts"
import NextAuth from "next-auth"
import Postmark from "next-auth/providers/postmark"

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Postmark({
      async generateVerificationToken() {
        return crypto.randomUUID()
      },
    }),
  ],
})
```

### Normalizing Email Addresses

By default, Auth.js will normalize the email address. It treats the address as case-insensitive (which is technically not compliant to the [RFC 2821 spec](https://datatracker.ietf.org/doc/html/rfc2821), but in practice this causes more problems than it solves, i.e. when looking up users by e-mail from databases.) and also removes any secondary email address that may have been passed in as a comma-separated list. You can apply your own normalization via the `normalizeIdentifier` method on the `Postmark` provider. The following example shows the default behavior:

```ts filename="./auth.ts"
import NextAuth from "next-auth"
import Postmark from "next-auth/providers/postmark"

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [
    Postmark({
      normalizeIdentifier(identifier: string): string {
        // Get the first two elements only,
        // separated by `@` from user input.
        let [local, domain] = identifier.toLowerCase().trim().split("@")
        // The part before "@" can contain a ","
        // but we remove it on the domain part
        domain = domain.split(",")[0]
        return `${local}@${domain}`

        // You can also throw an error, which will redirect the user
        // to the sign-in page with error=EmailSignin in the URL
        // if (identifier.split("@").length > 2) {
        //   throw new Error("Only one email allowed")
        // }
      },
    }),
  ],
})
```

<Callout type="warning">
  Always make sure this returns a single e-mail address, even if multiple ones
  were passed in.
</Callout>
